#Requires AutoHotkey v2.0-

; 偷了别人的一个GDI类 以便绘制
; 修改为 AutoHotkey v2 版本并 debug

class GDI ; thanks dwitter, RUNIE, FeiYue
{
    /*================================================================
      这个类可以想象为一个画者，他需要画板hwnd，还要确定画的大小，
      可以选择画笔、画刷、粗细、颜色等等。
      ================================================================
    */
    __New(hWnd, CliWidth:=0, CliHeight:=0)
    {
        ; 如果初始化时没有指定宽和高，则自动获取
        if !(CliWidth && CliHeight)
        {
            VarSetStrCapacity(Rect, 16)
            DllCall("GetClientRect", "Ptr", hWnd, "Ptr", &Rect)
            CliWidth := NumGet(Rect, 8, "Int")
            CliHeight := NumGet(Rect, 12, "Int")
        }
        this.hWnd := hWnd
        this.CliWidth := CliWidth
        this.CliHeight := CliHeight
        this.hDC := DllCall("GetDC", "UPtr", this.hWnd, "UPtr")
        this.hMemDC := DllCall("CreateCompatibleDC", "UPtr", this.hDC, "UPtr")
        this.hBitmap := DllCall("CreateCompatibleBitmap", "UPtr", this.hDC, "Int", CliWidth, "Int", CliHeight, "UPtr")
        this.hOriginalBitmap := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", this.hBitmap)
        DllCall("ReleaseDC", "UPtr", this.hWnd, "UPtr", this.hDC)
        ; 系统坐标系 Y 轴可以反转。
        this.invert := false
    }

    __Delete() {
        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", this.hOriginalBitmap)
        DllCall("DeleteObject", "UPtr", this.hBitmap)
        DllCall("DeleteObject", "UPtr", this.hMemDC)
    }
  
    Resize(w, h)
    {
        this.CliWidth := w
        this.CliHeight := h
        this.hDC := DllCall("GetDC", "UPtr", this.hWnd, "UPtr")
        this.hBitmap := DllCall("CreateCompatibleBitmap", "UPtr", this.hDC, "Int", w, "Int", h, "UPtr")
        hPrevBitmap := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", this.hBitmap)
        DllCall("DeleteObject", "UPtr", hPrevBitmap)
        DllCall("ReleaseDC", "UPtr", this.hWnd, "UPtr", this.hDC)
    }

    BitBlt(x:=0, y:=0, w:=0, h:=0)
    {
        w := w ? w : this.CliWidth
        h := h ? h : this.CliHeight
        this.hDC := DllCall("GetDC", "UPtr", this.hWnd, "UPtr")
        DllCall("BitBlt", "UPtr", this.hDC, "Int", x, "Int", y
        , "Int", w, "Int", h, "UPtr", this.hMemDC, "Int", 0, "Int", 0, "UInt", 0xCC0020) ;SRCCOPY
        DllCall("ReleaseDC", "UPtr", this.hWnd, "UPtr", this.hDC)
    }
  
    UpdateLayeredWindow(x:=0, y:=0, w:=0, h:=0, color:=0, Alpha:=255)
    {
        w := w ? w : this.CliWidth
        h := h ? h : this.CliHeight
        DllCall("UpdateLayeredWindow", "UPtr", this.hWnd, "UPtr", 0
        , "Int64*", x|y<<32, "Int64*", w|h<<32
        , "UPtr", this.hMemDC, "Int64*", 0, "UInt", color
        , "UInt*", Alpha<<16|1<<24, "UInt", 1)
    }
  
    DrawLine(x, y, x2, y2, Color, Width:=1)
    {
        Pen := GDI.Pen(Color, Width)
        DllCall("MoveToEx", "UPtr", this.hMemDC, "Int", this.TranslateX(x), "Int", this.TranslateY(y), "UPtr", 0)
        hOriginalPen := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", Pen.Handle, "UPtr")
        DllCall("LineTo", "UPtr", this.hMemDC, "Int", this.TranslateX(x2), "Int", this.TranslateY(y2))
        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", hOriginalPen, "UPtr")
    }
  
    SetPixel(x, y, Color)
    {
        x := this.TranslateX(x)
        y := this.TranslateY(y, 1) ; Move up 1 px if inverted (drawing "up" instead of down)
        DllCall("SetPixelV", "UPtr", this.hMemDC, "Int", x, "Int", y, "UInt", Color)
    }
  
    FillRectangle(x, y, w, h, Color, BorderColor:=-1)
    {
        if (w == 1 && h == 1)
          return this.SetPixel(x, y, Color)

        Pen := GDI.Pen(BorderColor < 0 ? Color : BorderColor)
        Brush := GDI.Brush(Color)

        ; Replace the original pen and brush with our own
        hOriginalPen := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", Pen.Handle, "UPtr")
        hOriginalBrush := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", Brush.Handle, "UPtr")

        x1 := this.TranslateX(x)
        y1 := this.TranslateY(y)
        x2 := this.TranslateX(x+w)
        y2 := this.TranslateY(y+h)

        DllCall("Rectangle", "UPtr", this.hMemDC
        , "Int", x1, "Int", y1
        , "Int", x2, "Int", y2)

        ; Reselect the original pen and brush
        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", hOriginalPen, "UPtr")
        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", hOriginalBrush, "UPtr")
    }

    FillEllipse(x, y, w, h, Color, BorderColor:=-1)
    {
        Pen := GDI.Pen(BorderColor < 0 ? Color : BorderColor)
        Brush := GDI.Brush(Color)

        ; Replace the original pen and brush with our own
        hOriginalPen := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", Pen.Handle, "UPtr")
        hOriginalBrush := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", Brush.Handle, "UPtr")

        x1 := this.TranslateX(x)
        y1 := this.TranslateY(y)
        x2 := this.TranslateX(x+w)
        y2 := this.TranslateY(y+h)

        DllCall("Ellipse", "UPtr", this.hMemDC
        , "Int", x1, "Int", y1
        , "Int", x2, "Int", y2)

        ; Reselect the original pen and brush
        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", hOriginalPen, "UPtr")
        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", hOriginalBrush, "UPtr")
    }

    TranslateX(X)
    {
        return Floor(X)
    }

    TranslateY(Y, Offset:=0)
    {
        return this.Invert ? this.CliHeight - Floor(Y) - Offset : Floor(Y)
    }

    class Pen
    {
        __New(Color, Width:=1, Style:=0)
            {
                this.Handle := DllCall("CreatePen", "Int", Style, "Int", Width, "UInt", Color, "UPtr")
            }

        __Delete()
        {
            DllCall("DeleteObject", "UPtr", this.Handle)
        }
    }

    class Brush
    {
        __New(Color)
        {
            this.Handle := DllCall("CreateSolidBrush", "UInt", Color, "UPtr")
        }

        __Delete()
        {
            DllCall("DeleteObject", "UPtr", this.Handle)
        }
    }
}
